#include"StdAfx.h"

pgRender::pgRender()
{
	m_pSwapChain = NULL;			// BackBuffer swapchain
	m_pd3dDevice = NULL;			// D3D11 device
	m_pImmediateContext = NULL;		// Device Context
	m_pRenderTargetView = NULL;
	m_driverType = D3D_DRIVER_TYPE_NULL;
	m_featureLevel = D3D_FEATURE_LEVEL_11_0;
	m_pRasterState = NULL;
	m_bIsInitialized = false;

	m_apPixelShaders = NULL;
	m_apVertexShaders = NULL;

	m_pVertexMap = NULL;
	m_pPixelMap = NULL;

	 m_nPixelShaderCount = 0;
	 m_nVertexShaderCount = 0;

	 m_pViewCamera = NULL;
}

pgRender::~pgRender()
{
}

HRESULT pgRender::Init()
{
	PG_LOG_INFO_EX( TOPIC_PGRENDERER,"pgRenderer is initializing...");
	HRESULT hr = S_OK;

    RECT rc;
	GetClientRect(g_PG_pSGameInfo->GetWindowHandle(), &rc);
    //UINT width = 1680 ;//rc.right - rc.left;
    //UINT height = 1050;//rc.bottom - rc.top;

    UINT createDeviceFlags = 0;
#ifdef _DEBUG
    createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

    D3D_DRIVER_TYPE driverTypes[] =
    {
        D3D_DRIVER_TYPE_HARDWARE,
        D3D_DRIVER_TYPE_WARP,
        D3D_DRIVER_TYPE_REFERENCE,
    };

    UINT numDriverTypes = ARRAYSIZE(driverTypes);

    D3D_FEATURE_LEVEL featureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
    };
	UINT numFeatureLevels = ARRAYSIZE( featureLevels );

    DXGI_SWAP_CHAIN_DESC sd;
    ZeroMemory( &sd, sizeof( sd ) );
    sd.BufferCount = 1;
	sd.BufferDesc.Width = g_PG_pSGameInfo->GetScreenWidth();
	sd.BufferDesc.Height = g_PG_pSGameInfo->GetScreenHeight();
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferDesc.RefreshRate.Numerator = 0;
    sd.BufferDesc.RefreshRate.Denominator = 1;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	sd.OutputWindow = g_PG_pSGameInfo->GetWindowHandle();
    sd.SampleDesc.Count = 1;
    sd.SampleDesc.Quality = 0;
	sd.Windowed = g_PG_pSGameInfo->IsWindowed();
	sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

    for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
    {
        m_driverType = driverTypes[driverTypeIndex];
        hr = D3D11CreateDeviceAndSwapChain( NULL, m_driverType, NULL, createDeviceFlags, featureLevels, numFeatureLevels,
                                            D3D11_SDK_VERSION, &sd, &m_pSwapChain, &m_pd3dDevice, &m_featureLevel, &m_pImmediateContext );
        if( SUCCEEDED( hr ) )
            break;
    }
    if( FAILED( hr ) )
	{
		PG_DX_LOG_ERROR(hr, __FILE__, __LINE__);
		PG_ERROR(ER_D3D_INIT_FAILURE);
        return hr;
	}

	D3D11_RASTERIZER_DESC rasterDesc;

	rasterDesc.AntialiasedLineEnable = false;
	rasterDesc.CullMode = D3D11_CULL_BACK;
	rasterDesc.DepthBias = 0;
	rasterDesc.DepthBiasClamp = 0.0f;
	rasterDesc.DepthClipEnable = true;
	rasterDesc.FillMode = D3D11_FILL_SOLID;
	rasterDesc.FrontCounterClockwise = false;
	rasterDesc.MultisampleEnable = false;
	rasterDesc.ScissorEnable = false;
	rasterDesc.SlopeScaledDepthBias = 0.0f;

	m_pd3dDevice->CreateRasterizerState(&rasterDesc, &m_pRasterState);

    // Create a render target view
    ID3D11Texture2D* pBackBuffer = NULL;
    hr = m_pSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&pBackBuffer );
    if( FAILED( hr ) )
	{
		PG_DX_LOG_ERROR(hr, __FILE__, __LINE__);
		PG_ERROR(ER_D3D_INIT_FAILURE);
        return hr;
	}

    hr = m_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &m_pRenderTargetView );
    pBackBuffer->Release();
    if( FAILED( hr ) )
    {
		PG_DX_LOG_ERROR(hr, __FILE__, __LINE__);
		PG_ERROR(ER_D3D_INIT_FAILURE);
        return hr;
	}

   m_pImmediateContext->OMSetRenderTargets( 1, &m_pRenderTargetView, NULL );

    // Setup the viewport
    D3D11_VIEWPORT vp;
	vp.Width = (FLOAT)g_PG_pSGameInfo->GetScreenWidth();
    vp.Height = (FLOAT)g_PG_pSGameInfo->GetScreenHeight();
    vp.MinDepth = 0.0f;
    vp.MaxDepth = 1.0f;
    vp.TopLeftX = 0;
    vp.TopLeftY = 0;
    m_pImmediateContext->RSSetViewports( 1, &vp );


	PG_LOG_INFO_EX(TOPIC_PGRENDERER, "DirectX was intialized successfully!");

	m_bIsInitialized = true;

	g_PG_pFrameworkEventManager->RegisterListener(this);

	PG_LOG_INFO_EX(TOPIC_PGRENDERER, "Loading shaders...!");
	InitShaders();

	PG_LOG_INFO_EX(TOPIC_PGRENDERER, "Creating default camera!");

	m_pViewCamera = new NEW_MEM_LEAK pgCamera(pgMat44::Identity(), pgVec3(0.0f, 0.0f, 4.0f), 0.1f, 500.0f,
		g_PG_pSGameInfo->GetScreenWidth() / g_PG_pSGameInfo->GetScreenHeight(), 65.0f);

	PG_LOG_INFO_EX(TOPIC_PGRENDERER, "Finished initializing renderer!");
	return true;
}

HRESULT pgRender::Release()
{
	PG_SAFE_DELETE(m_pViewCamera);

	ReleaseShaders();

	if(m_pImmediateContext) 
		m_pImmediateContext->ClearState();

	PG_SAFE_RELEASE(m_pRenderTargetView);
	PG_SAFE_RELEASE(m_pSwapChain);
	PG_SAFE_RELEASE(m_pImmediateContext);
	PG_SAFE_RELEASE(m_pd3dDevice);
	PG_SAFE_RELEASE(m_pRasterState);

	g_PG_pFrameworkEventManager->UnregisterListener(this);

	return S_OK;
}

void pgRender::Render()
{
	if (!m_bIsInitialized)
		return;

	float ClearColor[4] = {0.125f, 0.125f, 0.125f, 1.0f}; //red,green,blue,alpha
    m_pImmediateContext->ClearRenderTargetView(m_pRenderTargetView, ClearColor);
	
	std::list<IRenderableObject*>::iterator it = m_renderNodes.begin();

	while(it != m_renderNodes.end())
	{
		(*it)->PreRender();
		(*it)->Render();
		(*it)->PostRender();
		++it;
	}

	
	m_pSwapChain->Present(0,0);

	m_renderNodes.clear();
}
void pgRender::OnEvent(IEvent *eEvent)
{
	//PG_LOG_INFO("pgRenderer received event!");
}

bool pgRender::InitShaders()
{
	int vertexShaderCount = 0;
	std::string *pVertexShaderFiles = pgPath::GetFilesInDirectory(L".\\Data\\Shaders\\VertexShaders\\*", vertexShaderCount);

	int pixelShaderCount = 0;
	std::string *pPixelShaderFiles = pgPath::GetFilesInDirectory(L".\\Data\\Shaders\\PixelShaders\\*", pixelShaderCount);

	m_nPixelShaderCount = pixelShaderCount;
	m_nVertexShaderCount = vertexShaderCount;

	m_apPixelShaders = new NEW_MEM_LEAK pgPixelShader[pixelShaderCount];
	m_apVertexShaders = new NEW_MEM_LEAK pgVertexShader[vertexShaderCount];

	m_pVertexMap = new NEW_MEM_LEAK std::map<const char*, unsigned int>();
	m_pPixelMap = new NEW_MEM_LEAK std::map<const char*, unsigned int>();

	for (int i = 0; i < vertexShaderCount; ++i)
	{
		const char* original = pVertexShaderFiles[i].insert(0, ".\\Data\\Shaders\\VertexShaders\\").c_str();
		size_t origsize = strlen(original) + 1;
		const size_t newsize = 100;
		size_t convertedChars = 0;
		wchar_t wcstring[newsize];
		mbstowcs_s(&convertedChars, wcstring, origsize, original, _TRUNCATE);
		m_charIntPair = std::make_pair(pgPath::GetFileName(original).c_str(), i);
		m_pVertexMap->insert(m_charIntPair);
		m_apVertexShaders[i].CompileFromFile(wcstring);
	}

	for (int i = 0; i < pixelShaderCount; ++i)
	{
		const char* original = pPixelShaderFiles[i].insert(0, ".\\Data\\Shaders\\PixelShaders\\").c_str();
		size_t origsize = strlen(original) + 1;
		const size_t newsize = 100;
		size_t convertedChars = 0;
		wchar_t wcstring[newsize];
		mbstowcs_s(&convertedChars, wcstring, origsize, original, _TRUNCATE);
		m_charIntPair = std::make_pair(pgPath::GetFileName(original).c_str(), i);
		m_pPixelMap->insert(m_charIntPair);
		m_apPixelShaders[i].CompileFromFile(wcstring);
	}

	PG_SAFE_DELETE_ARRAY(pVertexShaderFiles);
	PG_SAFE_DELETE_ARRAY(pPixelShaderFiles);


	return true;
}

unsigned int pgRender::GetIndexOfPixelShader(const char* szName)
{
	std::map<const char*, unsigned int>::iterator i = m_pPixelMap->find(szName);

	if (i == m_pPixelMap->end())
	{
		PG_LOG_WARNING_EX(TOPIC_PGRENDERER, "Index of requested pixel shader: %s not found! Using nothing.fx", szName);
		return 0;
	}
	else
	{
		return i->second;
	}
}
	
unsigned int pgRender::GetIndexOfVertexShader(const char* szName)
{
	std::map<const char*, unsigned int>::iterator i = m_pVertexMap->find(szName);

	if (i == m_pVertexMap->end())
	{
		PG_LOG_WARNING_EX(TOPIC_PGRENDERER, "Index of requested vertex shader: %s not found! Using nothing.fx!", szName);
		return 0;
	}
	else
	{
		return i->second;
	}
}

void pgRender::ReleaseShaders()
{
	for (int i = 0; i < m_nPixelShaderCount; ++i)
	{
		m_apPixelShaders[i].ReleaseShader();
	}

	for (int i = 0; i < m_nVertexShaderCount; ++i)
	{
		m_apVertexShaders[i].ReleaseShader();
	}

	PG_SAFE_DELETE_ARRAY(m_apPixelShaders);
	PG_SAFE_DELETE_ARRAY(m_apVertexShaders);

	PG_SAFE_DELETE(m_pPixelMap);
	PG_SAFE_DELETE(m_pVertexMap);
}

void pgRender::AddRenderNode(IRenderableObject *pNode)
{
	if (pNode)
	{
		m_renderNodes.push_back(pNode);
	}
	else
	{
		PG_LOG_WARNING_EX(TOPIC_PGRENDERER, "Trying to add a null RenderableObject to the renderqueue!");
	}
}


